/*
 * 
 *  This file is part of Sourceprojects.org Thracia.
 *  Copyright 2010 noctarius
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 */
package org.sourceprojects.thracia.output.writer;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.List;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.sourceprojects.thracia.output.sender.QueuedReport;
import org.sourceprojects.thracia.report.beans.Property;
import org.sourceprojects.thracia.report.beans.Report;
import org.sourceprojects.thracia.report.beans.Stacktrace;
import org.sourceprojects.thracia.report.beans.Thread;
import org.sourceprojects.thracia.report.beans.Threadgroup;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * @author noctarius
 * 
 */
public class XsqlWriter<T extends Report, E extends Object> extends
		AbstractOutputWriter<T, E> {
	public XsqlWriter(T report) {
		super(report);
	}

	@Override
	public QueuedReport flush() {
		return writeXml();
	}

	private Element getJvmProperties(T report, Document doc) {
		final Element jvm = doc.createElement("jvm");

		for (final Property env : report.getJvm().getProperties()) {
			final Element prop = doc.createElement("property");

			prop.setAttribute("name", env.getName());
			prop.setTextContent(env.getValue());

			jvm.appendChild(prop);
		}

		final Element env = getProperties(report, doc);
		jvm.appendChild(env);

		return jvm;
	}

	private Element getProperties(T report, Document doc) {
		final Element properties = doc.createElement("environment");

		for (final Property env : report.getJvm().getEnvironment()) {
			final Element prop = doc.createElement("property");

			prop.setAttribute("name", env.getName());
			prop.setTextContent(env.getValue());

			properties.appendChild(prop);
		}

		return properties;
	}

	private Element getStacktrace(T report, Document doc) {
		return getStacktrace(report.getStacktrace(), doc);
	}

	private Element getStacktrace(List<Stacktrace> stacktraces, Document doc) {
		final Element stacktrace = doc.createElement("stacktrace");

		for (final Stacktrace element : stacktraces) {
			Element entry;

			if (element.getStacktrace() != null
					&& element.getStacktrace().size() > 0) {
				entry = getStacktrace(element.getStacktrace(), doc);
			} else {
				entry = doc.createElement("element");

				entry.setAttribute("class", element.getClassname());
				entry.setAttribute("method", element.getMethod());
				entry.setAttribute("line", String.valueOf(element.getLine()));
			}

			stacktrace.appendChild(entry);
		}

		return stacktrace;
	}

	private Element getThreadGroup(Threadgroup group, Document doc) {
		final Element threadgroup = doc.createElement("threadgroup");

		threadgroup.setAttribute("name", group.getName());

		for (final Thread t : group.getThreads()) {

			final Element thread = doc.createElement("thread");

			thread.setAttribute("name", t.getName());
			thread.setAttribute("id", String.valueOf(t.getId()));
			thread.setAttribute("status", t.getStatus());

			threadgroup.appendChild(thread);
		}

		for (final Threadgroup tg : group.getThreadgroups()) {
			final Element result = getThreadGroup(tg, doc);
			threadgroup.appendChild(result);
		}

		return threadgroup;
	}

	private Element getThreads(T report, Document doc) {
		final Element threads = doc.createElement("threads");

		final Element result = getThreadGroup(report.getThreads().get(0), doc);
		threads.appendChild(result);

		return threads;
	}

	@Override
	@SuppressWarnings("unchecked")
	protected E render() {
		try {
			final Document doc = createDocumentBuilder().newDocument();

			final Element root = doc.createElement("log");
			final Element record = doc.createElement("record");
			root.appendChild(record);

			final Element product = doc.createElement("product");
			product.setTextContent(getReport().getProduct());
			record.appendChild(product);

			final Element hash = doc.createElement("hash");
			hash.setTextContent(getReport().getHash());
			record.appendChild(hash);

			final Element checksum = doc.createElement("checksum");
			checksum.setTextContent(getReport().getClassbase().getChecksum());
			record.appendChild(checksum);

			final Element classbase = doc.createElement("classbase");
			classbase.setTextContent(getReport().getClassbase().getClassbase());
			record.appendChild(classbase);

			final Element classbasetype = doc.createElement("classbasetype");
			classbasetype.setTextContent(getReport().getClassbase().getType()
					.name());
			record.appendChild(classbasetype);

			final Element release = doc.createElement("release");
			release.setTextContent(getReport().getRelease());
			record.appendChild(release);

			final Element date = doc.createElement("date");
			date.setTextContent(getReport().getDate());
			record.appendChild(date);

			final Element millis = doc.createElement("millis");
			millis.setTextContent(String.valueOf(getReport().getMillis()));
			record.appendChild(millis);

			final Element sequence = doc.createElement("sequence");
			sequence.setTextContent(String.valueOf(getReport().getSequence()));
			record.appendChild(sequence);

			final Element logger = doc.createElement("logger");
			logger.setTextContent(getReport().getLogger());
			record.appendChild(logger);

			final Element trigger = doc.createElement("trigger");
			trigger.setTextContent(getReport().getTrigger());
			record.appendChild(trigger);

			final Element method = doc.createElement("method");
			method.setTextContent(getReport().getMethod());
			record.appendChild(method);

			final Element threadid = doc.createElement("threadid");
			threadid.setTextContent(String.valueOf(getReport().getThreadid()));
			record.appendChild(threadid);

			final Element priority = doc.createElement("priority");
			priority.setTextContent(String.valueOf(getReport().getPriority()));
			record.appendChild(priority);

			final Element errormsg = doc.createElement("errormsg");
			errormsg.setTextContent(getReport().getErrormsg());
			record.appendChild(errormsg);

			final Element stacktrace = getStacktrace(getReport(), doc);
			record.appendChild(stacktrace);

			final Element threads = getThreads(getReport(), doc);
			record.appendChild(threads);

			final Element properties = getJvmProperties(getReport(), doc);
			record.appendChild(properties);

			doc.appendChild(root);

			return (E) doc;
		} catch (Exception e) {
			return null;
		}
	}

	private QueuedReport writeXml() {
		QueuedReport report = null;

		try {
			removeExistingErrorLogsForReport();

			Document document = (Document) render();

			if (document == null)
				return null;

			final File file = getReportFile();
			final Writer writer = new FileWriter(file);

			final TransformerFactory factory = TransformerFactory.newInstance();
			final Transformer transformer = factory.newTransformer();
			transformer.setOutputProperty("omit-xml-declaration", "yes");
			transformer.setOutputProperty(OutputKeys.METHOD, "xml");
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(
					"{http://xml.apache.org/xslt}indent-amount", "3");
			final DOMSource source = new DOMSource(document);
			final StreamResult result = new StreamResult(writer);

			transformer.transform(source, result);

			report = new QueuedReport(file.getName());
		} catch (final IOException e) {
			e.printStackTrace();
		} catch (final TransformerException e) {
			e.printStackTrace();
		}

		return report;
	}

	@Override
	protected int getSequenceNumber(String data) {
		String line = data.substring(data.indexOf("<sequence>") + 10);
		line = line.substring(0, line.indexOf("</sequence>"));

		return Integer.parseInt(line);
	}
}
